package task

import (
	"fmt"
	"os"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/logger"
	fileutil "gitee.com/sansaniot/ssiot-core/utils"
	"gorm.io/gorm"

	"ssadmin/common/constant"
	"ssadmin/common/utils"
	"ssadmin/internal/admin/models"
)

// CleanData 清理数据库 (默认保留、按租户设置保留)
func CleanData() {
	db := env.Db
	if db == nil {
		return
	}
	logger.Info("执行数据清理.")
	defaultKeep := constant.DefaultDataKeepDays
	// 历史表删除的前提条件
	connActiveCount, isPartition := getHisCondition()
	// 按系统配置清理一遍
	clean(db, defaultKeep, "", connActiveCount, isPartition)
	// 按各租户配置的清理
	dkModel := &models.SysDataKeep{}
	dkModel.Orm = db
	keeps := make([]models.SysDataKeep, 0)
	_ = dkModel.FindList(&keeps)
	for _, dataKeep := range keeps {
		clean(db, dataKeep.Keep, dataKeep.AgencyId, connActiveCount, isPartition)
	}
}

var scan = map[string]interface{}{}
var scanArr = make([]map[string]interface{}, 0)

func getHisCondition() (connActiveCount int, isPartition bool) {
	env.Db.Raw("select count(1) from pg_stat_activity where state = 'active'").Scan(&scan)
	// 并发过载时不操作  减少压力
	connActiveCount = utils.GetCommUtil().GetIntValueFiled(scan, "count")
	env.Db.Raw("SELECT relkind FROM pg_class WHERE relname = 'sys_history_sensor'").Scan(&scan)
	// 判断分区表
	isPartition = len(scan) > 0 && scan["relkind"] == "p"
	return
}

func clean(db *gorm.DB, days int, agencyId string, connActiveCount int, isPartition bool) {
	timeLimit := utils.ConvtTimeToStr(time.Now().AddDate(0, 0, -days), utils.TIME_FMT_YMD_000)

	pathList := make([]string, 0)
	snList := make([]string, 0)
	scanArr = scanArr[0:0]
	if len(agencyId) > 0 {
		logger.Infof("清理租户%s,时间<%s(%v天以前)的数据", agencyId, timeLimit, days)
		db.Raw("delete from sys_operate_log where operate_time < ? and agency_id = ? ", timeLimit, agencyId).Scan(&scan)
		db.Raw("select sn from sys_dev where dept_id in (select dept_id from sys_user_dept_r where user_id = ?)", agencyId).Scan(&scanArr)
		for _, m := range scanArr {
			snList = append(snList, m["sn"].(string))
		}
		if len(snList) == 0 {
			return
		}
		db.Raw("delete from sys_alarm where create_time < ? and dev_sn in ? ", timeLimit, snList).Scan(&scan)
		db.Raw("delete from sys_notify_record where create_time < ? and sn in ? ", timeLimit, snList).Scan(&scan)
		if isPartition {
			db.Raw("delete from sys_history_system where dev_sn in ? and date_time < ? and update_time < ? ", snList, timeLimit[0:10], timeLimit).Scan(&scan)
		} else {
			db.Raw("delete from sys_history_system where dev_sn in ? and update_time < ? ", snList, timeLimit).Scan(&scan)
		}
		// 查询时间段内的文件
		scanArr = scanArr[0:0]
		db.Raw("select path from sys_resource where create_time < ? and agency_id = ? ", timeLimit, agencyId).Scan(&scanArr)
		for _, m := range scanArr {
			pathList = append(pathList, m["path"].(string))
		}
		db.Raw("delete from sys_resource where create_time < ? and agency_id = ? ", timeLimit, agencyId).Scan(&scan)
		// 历史表删除
		if connActiveCount < 100 {
			t := time.Now()
			if isPartition {
				db.Raw("delete from sys_history_sensor where dev_sn in ? and date_time < ? and update_time < ? ", snList, timeLimit[0:10], timeLimit).Scan(&scan)
			} else {
				db.Raw("delete from sys_history_sensor where dev_sn in ? and update_time < ? ", snList, timeLimit).Scan(&scan)
			}
			logger.Infof("清理历史数据耗时%v", time.Since(t).Round(time.Millisecond))
		}
	} else {
		logger.Infof("默认清理时间<%s(%v天以前)的数据", timeLimit, days)
		db.Raw("delete from sys_alarm where create_time < ? ", timeLimit).Scan(&scan)
		db.Raw("delete from sys_data_detail where create_time < ? ", timeLimit).Scan(&scan)
		db.Raw("delete from sys_notify_record where create_time < ? ", timeLimit).Scan(&scan)
		db.Raw("delete from sys_operate_log where operate_time < ? ", timeLimit).Scan(&scan)
		partType := env.ConfigOperator.GetString("service.device.parttype")
		if isPartition {
			if partType == "date" {
				scanArr = scanArr[0:0]
				timeBase := "sys_history_system_" + strings.ReplaceAll(strings.ReplaceAll(timeLimit[0:10], "-", "_"), " ", "_")
				db.Raw("SELECT relname FROM pg_class WHERE relname like 'sys_history_system%' and relispartition = 't' and relname < ?", timeBase).Scan(&scanArr)
				dropsql := "drop table %s"
				for _, m := range scanArr {
					tbname := m["relname"].(string)
					if err := db.Raw(fmt.Sprintf(dropsql, tbname)).Scan(&scan).Error; err == nil {
						logger.Infof("删除system分区子表%v", tbname)
					}
				}
			} else {
				db.Raw("delete from sys_history_system where date_time < ? and update_time < ? ", timeLimit[0:10], timeLimit).Scan(&scan)
			}
		} else {
			db.Raw("delete from sys_history_system where update_time < ? ", timeLimit).Scan(&scan)
		}

		// 查询时间段内的文件
		db.Raw("select path from sys_resource where create_time < ? ", timeLimit).Scan(&scanArr)
		for _, m := range scanArr {
			if m["path"] == nil {
				continue
			}

			pathList = append(pathList, m["path"].(string))
		}

		db.Raw("delete from sys_resource where create_time < ? ", timeLimit).Scan(&scan)

		// 历史表删除
		if connActiveCount < 100 {
			t := time.Now()
			if isPartition {
				if partType == "date" {
					scanArr = scanArr[0:0]
					timeBase := "sys_history_sensor_" + strings.ReplaceAll(strings.ReplaceAll(timeLimit[0:10], "-", "_"), " ", "_")
					db.Raw("SELECT relname FROM pg_class WHERE relname like 'sys_history_sensor%' and relispartition = 't' and relname < ?", timeBase).Scan(&scanArr)
					dropsql := "drop table %s"
					for _, m := range scanArr {
						tbname := m["relname"].(string)
						if err := db.Raw(fmt.Sprintf(dropsql, tbname)).Scan(&scan).Error; err == nil {
							logger.Infof("删除历史分区子表%v", tbname)
						}
					}
				} else {
					db.Raw("delete from sys_history_sensor where date_time < ? and update_time < ? ", timeLimit[0:10], timeLimit).Scan(&scan)
				}
			} else {
				db.Raw("delete from sys_history_sensor where update_time < ? ", timeLimit).Scan(&scan)
			}
			logger.Infof("清理历史数据耗时%v", time.Since(t).Round(time.Millisecond))
		}
	}
	// 清理文件
	for _, p := range pathList {
		if strings.Contains(p, "/scada") || isIgnorePath(p) {
			continue
		}
		// 删原文件
		_ = os.Remove(constant.FilePath + "/" + p)
		// 删缩略图
		suffix := fileutil.GetExt(p)
		withoutSuffix := strings.ReplaceAll(p, suffix, "")
		thumbnailImagePath := withoutSuffix + constant.ThumbnailConcat + suffix
		_ = os.Remove(constant.FilePath + "/" + thumbnailImagePath)
		logger.Infof("删除文件%v, %v", p, thumbnailImagePath)
	}
}
